/*
 *   Copyright 2012, Thomas Kerber
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */
package milk.jpatch.classLevel;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import milk.jpatch.CPoolMap;
import milk.jpatch.Patch;
import milk.jpatch.Util;

import org.apache.bcel.classfile.Method;

/**
 * Denotes a patch of the methods list.
 * @author Thomas Kerber
 * @version 1.0.0
 */
public class MethodsPatch implements Patch<List<Method>>{
    
    private static final long serialVersionUID = 3810465079846250903L;
    
    /**
     * The component patches.
     */
    private List<MethodPatch> patches;
    
    /**
     * Creates.
     * @param patches The component patches.
     */
    public MethodsPatch(List<MethodPatch> patches){
        this.patches = patches;
    }
    
    /**
     * Generates.
     * @param old The old methods.
     * @param new_ The new methods.
     * @param map The cpool map.
     * @return The patch.
     */
    public static MethodsPatch generate(Method[] old, Method[] new_,
            CPoolMap map){
        List<MethodPatch> patches = new ArrayList<MethodPatch>();
        
        // Find method identifiers
        Set<String> identifiers = new HashSet<String>();
        for(Method m : old)
            identifiers.add(Util.getMethodIdentifier(m));
        for(Method m : new_)
            identifiers.add(Util.getMethodIdentifier(m));
        
        // Create one patch for each identifier
        for(String iden : identifiers){
            // Find methods
            Method oldM = null;
            Method newM = null;
            for(Method m : old){
                if(Util.getMethodIdentifier(m).equals(iden)){
                    oldM = m;
                    break;
                }
            }
            for(Method m : new_){
                if(Util.getMethodIdentifier(m).equals(iden)){
                    newM = m;
                    break;
                }
            }
            
            // And generate patch according to what was found.
            if(oldM == null && newM == null)
                continue; // Theoretically impossible, but hey.
            else if(oldM == null){
                patches.add(MethodAdd.generate(newM));
            }
            else if(newM == null){
                patches.add(MethodRem.generate(oldM));
            }
            else{
                patches.add(MethodModif.generate(oldM, newM, map));
            }
        }
        return new MethodsPatch(patches);
    }
    
    /**
     * 
     * @return The component patches.
     */
    public List<MethodPatch> getPatches(){
        return patches;
    }
    
    @Override
    public List<Method> patch(List<Method> methods, CPoolMap map){
        for(MethodPatch p : patches)
            methods = p.patch(methods, map);
        return methods;
    }
    
}
